package main
import "C"

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/Unknwon/goconfig"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

// 定义一个全局对象db
var db *sqlx.DB
var mysql_host string
var mysql_port string
var mysql_user string
var mysql_passwd string
var mysql_db string
var conf *goconfig.ConfigFile

// 定义一个初始化数据库的函数
func initDB() (err error) {
	mysql_host, _ = conf.GetValue("MYSQL", "MYSQL_HOST")
	mysql_port, _ = conf.GetValue("MYSQL", "MYSQL_PORT")
	mysql_user, _ = conf.GetValue("MYSQL", "MYSQL_USER")
	mysql_passwd, _ = conf.GetValue("MYSQL", "MYSQL_PASSWD")
	mysql_db, _ = conf.GetValue("MYSQL", "MYSQL_DATABASE")
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/", mysql_user, mysql_passwd, mysql_host, mysql_port)
	// 不会校验账号密码是否正确
	// 注意！！！这里不要使用:=，我们是给全局变量赋值，然后在main函数中使用全局变量db
	db, err = sqlx.Open("mysql", dsn)
	if err != nil {
		return err
	}
	// 尝试与数据库建立连接（校验dsn是否正确）
	err = db.Ping()
	if err != nil {
		return err
	}

	create_database := "create database if not exists `" + mysql_db + "` default character set utf8mb4"
	_, err = db.Exec(create_database)
	if err != nil {
		return err
	}

	db.Close()

	dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True", mysql_user, mysql_passwd, mysql_host, mysql_port, mysql_db)

	db, err = sqlx.Open("mysql", dsn)
	if err != nil {
		return err
	}
	// 尝试与数据库建立连接（校验dsn是否正确）
	err = db.Ping()
	if err != nil {
		return err
	}

	create_problems := "create table if not exists problems(" +
		"problem_id int primary key auto_increment," +
		"title text," +
		"description text," +
		"desc_input text," +
		"desc_output text," +
		"stdin text," +
		"stdout text," +
		"time_limit int," +
		"memory_limit int," +
		"compare_flag int," +
		"init_ref_id char(20)," +
		"data_ref_id char(20)," +
		"spj_ref_id char(20)," +
		"ruler int," +
		"query_set int default 0," +
		"judge_type int," +
		"eps char(20)" +
		") DEFAULT CHARSET=utf8mb4 auto_increment=1000;"
	_, err = db.Exec(create_problems)
	if err != nil {
		return err
	}

	create_status := "create table if not exists status(" +
		"runid int not null primary key auto_increment," +
		// 该列为对应的redis messageID
		"check_id char(20)," +
		"problem_id int," +
		"username varchar(30)," +
		"language char(10)," +
		"result char(30)," +
		"use_time int default -1," +
		"use_memory int default -1," +
		"error text," +
		"length int," +
		"pass_example tinyint default -1," +
		"total_example tinyint default -1," +
		"submit_time datetime," +
		"src text," +
		"extern_info text," +
		"recv_data text" +
		") DEFAULT CHARSET=utf8mb4;"
	_, err = db.Exec(create_status)
	if err != nil {
		return err
	}

	return nil
}

type StatusData struct {
	Runid      int       `json:"runid" db:"runid"`
	Username   string    `json:"username" db:"username"`
	Result     string    `json:"result" db:"result"`
	UseTime    int       `json:"use_time" db:"use_time"`
	UseMemory  int64     `json:"use_memory" db:"use_memory"`
	Length     int       `json:"length" db:"length"`
	SubmitTime time.Time `json:"submit_time" db:"submit_time"`
	ProblemId  int       `json:"problem_id" db:"problem_id"`
	Language   string    `json:"language" db:"language"`
}

type StatusDetailData struct {
	Runid      int       `json:"runid" db:"runid"`
	Username   string    `json:"username" db:"username"`
	Result     string    `json:"result" db:"result"`
	UseTime    int       `json:"use_time" db:"use_time"`
	UseMemory  int64     `json:"use_memory" db:"use_memory"`
	Length     int       `json:"length" db:"length"`
	SubmitTime time.Time `json:"submit_time" db:"submit_time"`
	Pass       int8      `json:"pass_example" db:"pass_example"`
	Total      int8      `json:"total_example" db:"total_example"`
	Err        string    `json:"error" db:"error"`
	Src        string    `json:"src" db:"src"`
	ProblemId  int       `json:"problem_id" db:"problem_id"`
	Language   string    `json:"language" db:"language"`
	ExternInfo string    `json:"extern_info" db:"extern_info"`
	RecvData   string	 `db:"recv_data"`
}

type Problem struct {
	ProblemId int    `json:"problem_id" db:"problem_id"`
	Title     string `json:"title" db:"title"`
}

type ProblemDetail struct {
	ProblemId   int    `json:"problem_id" db:"problem_id"`
	Title       string `json:"title" db:"title"`
	Description string `json:"description" db:"description"`
	DescInput   string `json:"desc_input" db:"desc_input"`
	DescOutput  string `json:"desc_output" db:"desc_output"`
	Stdin       string `json:"stdin" db:"stdin"`
	Stdout      string `json:"stdout" db:"stdout"`
	TimeLimit   int    `json:"time_limit" db:"time_limit"`
	MemoryLimit int    `json:"memory_limit" db:"memory_limit"`
	CompareFlag int    `json:"compare_flag" db:"compare_flag"`
	InitRefId	string `json:"init_ref_id" db:"init_ref_id"`
	DataRefId   string `json:"data_ref_id" db:"data_ref_id"`
	SpjRefId    string `json:"spj_ref_id" db:"spj_ref_id"`
	Ruler       int    `json:"ruler" db:"ruler"`
	QuerySet    int    `json:"query_set" db:"query_set"`
	JudgeType   int    `json:"judge_type" db:"judge_type"`
	Eps         string `json:"eps" db:"eps"`
}

type BackupCodeInfo struct {
	Id          string 		`json:"id"`
	Code        string 		`json:"code"`
	Language    string 		`json:"language"`
	JudgeType   int    		`json:"judge_type"`
	ProblemId   string 		`json:"problem_id"`
	Stdin       []string	`json:"stdin"` 
	Stdout      []string	`json:"stdout"` 
	InitRefId	string		`json:"init_ref_id"`
	DataRefId   string 		`json:"data_ref_id"`
	SpjRefId    string 		`json:"spj_ref_id"`
	Time        int    		`json:"time"`
	Memory      int64  		`json:"memory"`
	Compare     int    		`json:"compare"`
	Eps         string 		`json:"eps"`
	Ruler       int    		`json:"ruler"`
	QuerySet	int	   		`json:"query_set"`
	ResponseUrl string 		`json:"response_url"`
}

// 供模板文件内使用
func intAndint(x int, y int) bool {
	return (x & y) != 0
}

// 在线测试接口
func onlineTest(w http.ResponseWriter, r *http.Request) {
	var err error
	var problem ProblemDetail
	var codeInfo BackupCodeInfo
	var temp map[string]interface{}
	w.Header().Set("content-type", "application/json")
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("post data read error:%v", err)
		fmt.Fprint(w, `{"code":170, "msg":"post data read error"}`)
		return
	}
	err = json.Unmarshal(body, &temp)
	if err != nil {
		log.Printf("unmarshal post data(temp) error:%v", err)
		fmt.Fprint(w, `{"code":171, "msg":"unmarshal post data error"}`)
		return
	}
	err = json.Unmarshal(body, &codeInfo)
	if err != nil {
		log.Printf("unmarshal post data(codeInfo) error:%v", err)
		fmt.Fprint(w, `{"code":171, "msg":"unmarshal post data error"}`)
		return
	}
	codeInfo.Code = temp["code"].(string)
	err = db.Get(&problem, problemDetailSql, temp["problem_id"].(string))
	if err != nil {
		log.Printf("db select(table=problems) error:%v", err)
		fmt.Fprintf(w, `{"code":172, "msg":"db error"}`)
		return
	}
	codeInfo.Compare = problem.CompareFlag
	codeInfo.Eps = problem.Eps
	codeInfo.JudgeType = problem.JudgeType
	codeInfo.Memory = int64(problem.MemoryLimit)
	codeInfo.Time = problem.TimeLimit
	codeInfo.Ruler = 1 // OI_CONTENT
	codeInfo.ResponseUrl = ""
	if problem.InitRefId == "" {
		codeInfo.InitRefId = strconv.Itoa(problem.ProblemId)
	} else {
        codeInfo.InitRefId = problem.InitRefId
    }
	if problem.DataRefId == "" {
		codeInfo.DataRefId = strconv.Itoa(problem.ProblemId)
	} else {
		codeInfo.DataRefId = problem.DataRefId
    }
	if problem.SpjRefId == "" {
		codeInfo.SpjRefId = strconv.Itoa(problem.ProblemId)
	} else {
		codeInfo.SpjRefId = problem.SpjRefId
    }
	codeInfo.ProblemId = "" // 不缓存
	codeInfo.QuerySet = 127 // 所有结果集
	c := http.Client{
		Timeout: time.Second * 5,
	}
	bytesData, err := json.Marshal(codeInfo)
	if err != nil {
		log.Printf("json marshal data(codeInfo) error:%v", err)
		fmt.Fprintf(w, `{"code":170, "msg":"upload data error"}`)
		return
	}
	req, err := http.NewRequest("POST", backup_interface_url, bytes.NewReader(bytesData))
	if err != nil {
		log.Printf("new request online_interface error:%v", err)
		fmt.Fprintf(w, `{"code":170, "msg":"upload data error"}`)
		return
	}
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	resp, err := c.Do(req)
	if err != nil {
		log.Printf("post online_interface error:%v", err)
		fmt.Fprintf(w, `{"code":170, "msg":"upload data error"}`)
		return
	}
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("post-response online_interface data read error:%v", err)
		fmt.Fprint(w, `{"code":170, "msg":"response data read error"}`)
		return
	}
	w.Write(body)
}

type ResultInfo struct {
	Result     string      `json:"result" db:"result"`
	Time       int         `json:"time" db:"use_time"`
	Memory     int64       `json:"memory" db:"use_memory"`
	Pass       int8        `json:"pass_example" db:"pass_example"`
	Total      int8        `json:"test_example" db:"total_example"`
	Error      string      `json:"error" db:"error"`
	Runid      int         `json:"id" db:"runid"`
	RecvData   string	   `db:"recv_data"`
	ExternInfo interface{} `json:"extern_info" db:"extern_info"`
}
type ResponseHttp struct {
	Code int         `json:"code"`
	Data interface{} `json:"data"`
	Msg  string      `json:"msg"`
	Id   string      `json:"id"`
}

// 接收判题结果
func receiveResult(w http.ResponseWriter, r *http.Request) {
	if r.Method == "POST" {
		w.Header().Set("content-type", "application/json")
		body, _ := ioutil.ReadAll(r.Body)
		var result ResponseHttp
		var err error
		log.Printf("receive data:%s", string(body))
		err = json.Unmarshal(body, &result)
		if err != nil {
			log.Printf("unmarshal error:%v", err)
			fmt.Fprintf(w, ``)
			return
		}
		var resultInfo ResultInfo
		resultInfo.RecvData = string(body)
		resultInfo.Runid, err = strconv.Atoi(result.Id)
		if err != nil {
			log.Printf("reponse data-id parse error:%s[%v]", result.Id, err)
			return
		}
		if result.Code != 0 {
			log.Printf("response data-code not equal zero:%d", result.Code)
			resultInfo.Error = result.Msg
			resultInfo.Result = "PARAMS_ERROR"
			if err != nil {
				log.Printf("parse data-msg error:%s", resultInfo.Error)
				return
			}
			_, err = db.Exec(statusUpdateSql, resultInfo)
			if err != nil {
				log.Printf("database update error:%v", err)
				return
			}
			return
		}
		err = json.Unmarshal([]byte(result.Data.(string)), &resultInfo)
		if err != nil {
			log.Printf("resultInfo convert error:%v", err)
			log.Printf("data:%v", result.Data.(string))
			fmt.Fprintf(w, ``)
			return
		}
		if resultInfo.ExternInfo == nil {
			resultInfo.ExternInfo = ""
		} else {
			tempData, err := json.Marshal(resultInfo.ExternInfo)
			if err != nil {
				log.Printf("resultInfo.ExternInfo convert error:%v", err)
				log.Printf("resultInfo.ExternInfo:%v", resultInfo.ExternInfo)
			}
			resultInfo.ExternInfo = string(tempData)
		}
		_, err = db.NamedExec(statusUpdateSql, resultInfo)
		if err != nil {
			log.Printf("database update error:%v", err)
			fmt.Fprintf(w, ``)
			return
		}
	}
}

type UserCodeInfo struct {
	Username  string 		`json:"username" db:"username"`
	Code      string 		`json:"code" db:"code"`
	Language  string 		`json:"language" db:"language"`
	ProblemId string 		`json:"problem_id" db:"problem_id"`
	Stdin     interface{} 	`json:"stdin"` 		// stdin有可能是string,也有可能是[]string
}

const (
	// 尚未提交
	UNSUBMIT_STATUS = "UNSUBMIT"
	// 提交成功进入判题阶段
	JUDGE_STATUS = "JUDGING"
	// 提交失败进入等待阶段,一定时间后重传
	WAIT_STATUS = "WAITING"
)

// 代码提交接口
func submitCode(w http.ResponseWriter, r *http.Request) {
	var err error
	var problem ProblemDetail
	var codeInfo BackupCodeInfo
	var userInfo UserCodeInfo
	w.Header().Set("content-type", "application/json")
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Printf("post data read error:%v", err)
		fmt.Fprint(w, `{"code":170, "msg":"post data read error"}`)
		return
	}
	err = json.Unmarshal(body, &userInfo)
	if err != nil {
		log.Printf("unmarshal post data(userInfo) error:%v", err)
		fmt.Fprint(w, `{"code":171, "msg":"unmarshal post data error"}`)
		return
	}
	err = db.Get(&problem, problemDetailSql, userInfo.ProblemId)
	if err != nil {
		log.Printf("db select(table=problems) error:%v", err)
		fmt.Fprintf(w, `{"code":172, "msg":"db read error"}`)
		return
	}
	if problem.InitRefId == "" {
		codeInfo.InitRefId = strconv.Itoa(problem.ProblemId)
	} else {
        codeInfo.InitRefId = problem.InitRefId
    }
	if problem.DataRefId == "" {
		codeInfo.DataRefId = strconv.Itoa(problem.ProblemId)
	} else {
		codeInfo.DataRefId = problem.DataRefId
    }
	if problem.SpjRefId == "" {
		codeInfo.SpjRefId = strconv.Itoa(problem.ProblemId)
	} else {
		codeInfo.SpjRefId = problem.SpjRefId
    }
	codeInfo.ProblemId = strconv.Itoa(problem.ProblemId)
	codeInfo.Code = userInfo.Code
	codeInfo.Language = userInfo.Language
	codeInfo.Compare = problem.CompareFlag
	codeInfo.Eps = problem.Eps
	codeInfo.JudgeType = problem.JudgeType
	codeInfo.Memory = int64(problem.MemoryLimit)
	codeInfo.Time = problem.TimeLimit
	codeInfo.Ruler = problem.Ruler
	codeInfo.QuerySet = problem.QuerySet
	codeInfo.ResponseUrl = receive_url

	// 先存入数据库再进行提交，保证本地先拥有对应数据
	insertRet, err := db.Exec(statusInsertSql, userInfo.Username, UNSUBMIT_STATUS, len(codeInfo.Code), time.Now(), userInfo.ProblemId, userInfo.Code, userInfo.Language)
	if err != nil {
		log.Printf("db insert status error:%v", err)
		fmt.Fprintf(w, `{"code":180, "msg":"database update error"}`)
		return
	}
	runid, err := insertRet.LastInsertId()
	if err != nil {
		log.Printf("db get last_insert_id error:%v", err)
		fmt.Fprintf(w, `{"code":181, "msg":"database update error"}`)
		return
	}
	// 设置标识符,唯一确定返回的结果
	codeInfo.Id = strconv.FormatInt(runid, 10)

	// 数据库存储后进行网络代码提交
	c := http.Client{
		Timeout: time.Second * 5,
	}
	bytesData, err := json.Marshal(codeInfo)
	if err != nil {
		log.Printf("json marshal data(codeInfo) error:%v", err)
		fmt.Fprintf(w, `{"code":173, "msg":"submit data error"}`)
		return
	}
	req, err := http.NewRequest("POST", backup_interface_url, bytes.NewReader(bytesData))
	if err != nil {
		log.Printf("new request backup_interface error:%v", err)
		fmt.Fprintf(w, `{"code":173, "msg":"submit data error"}`)
		return
	}
	req.Header.Add("Connection", "close")
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	resp, err := c.Do(req)
	if err != nil {
		log.Printf("post backup_interface error:%v", err)
		fmt.Fprintf(w, `{"code":173, "msg":"submit data error"}`)
		return
	}
	body, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("post-response backup_interface data read error:%v", err)
		fmt.Fprint(w, `{"code":174, "msg":"submit-response data read error"}`)
		return
	}
	var temp map[string]interface{}
	err = json.Unmarshal(body, &temp)
	if err != nil {
		log.Printf("unmarshal post data(temp) error:%v", err)
		fmt.Fprint(w, `{"code":171, "msg":"unmarshal post data error"}`)
		return
	}
	ret, ok := temp["code"].(int)
	if ok {
		log.Printf("post-reponse code-parse error")
		fmt.Fprint(w, `{"code":171, "msg":"submit-response data parse error"}`)
		return
	}
	var submit_status string
	// 代码成功交付给消息队列,标记为判题状态
	if ret == 0 {
		submit_status = JUDGE_STATUS
	} else {
		// 交付失败标记为等待状态,将在另外的进程中重试
		submit_status = WAIT_STATUS
	}
	_, err = db.Exec(statusUpdateResSql, submit_status, codeInfo.ProblemId)
	if err != nil {
		log.Printf("db update status-result error:%v", err)
		fmt.Fprint(w, `{"code":180, "msg":"database update error"}`)
		return
	}
	fmt.Fprint(w, `{"code":0, "msg":"submit ok"}`)
}

type ShortResultData struct {
	Result string `json:"result"`
	Time   int    `json:"time"`
	Memory int64  `json:"memory"`
}

// 展示当前提交状态
func status(w http.ResponseWriter, r *http.Request) {
	var err error
	r.ParseForm()
	if r.Form.Has("runid") {
		var status StatusDetailData
		err = db.Get(&status, statusDetailSql, r.Form.Get("runid"))
		if err != nil {
			log.Printf("db select(table=status) error:%v", err)
			fmt.Fprintf(w, `Database Error`)
			return
		}
		t, err := template.ParseFiles(indexPath + "/status_detail.html")
		if err != nil {
			log.Printf("status_detail.html template parse error:%v", err)
			fmt.Fprintf(w, `template parse Error`)
			return
		}
		var first_wrong_output string
		var first_wrong_input string
		var first_wrong_stdout string
		var extern_info map[string]interface{}
		err = json.Unmarshal([]byte(status.ExternInfo), &extern_info)
		resultList := []ShortResultData{}
		outputList := []string{}
		stdoutList := []string{}
		if err != nil {
			log.Printf("parse extern_info field error : %v", err)
		} else {
			if value, ok := extern_info["first_wrong_input"]; ok && value != nil {
				first_wrong_input = value.(string)
			}
			if value, ok := extern_info["first_wrong_output"]; ok && value != nil {
				first_wrong_output = value.(string)
			}
			if value, ok := extern_info["first_wrong_stdout"]; ok && value != nil {
				first_wrong_stdout = value.(string)
			}

			if value, ok := extern_info["result_list"]; ok {
				dataBytes, err := json.Marshal(value)
				if err != nil {
					log.Printf("result_list marshal error:%v", err)
				} else {
					err = json.Unmarshal(dataBytes, &resultList)
					if err != nil {
						log.Printf("result_list parse error:%v", err)
					}
				}
			}

			if value, ok := extern_info["output_list"]; ok {
				dataBytes, err := json.Marshal(value)
				if err != nil {
					log.Printf("output_list marshal error:%v", err)
				} else {
					err = json.Unmarshal(dataBytes, &outputList)
					if err != nil {
						log.Printf("output_list parse error:%v", err)
					}
				}
			}

			if value, ok := extern_info["stdout_list"]; ok {
				dataBytes, err := json.Marshal(value)
				if err != nil {
					log.Printf("stdout_list marshal error:%v", err)
				} else {
					err = json.Unmarshal(dataBytes, &stdoutList)
					if err != nil {
						log.Printf("stdout_list parse error:%v", err)
					}
				}
			}
		}

		err = t.Execute(w, map[string]interface{}{
			"status":             status,
			"first_wrong_input":  	first_wrong_input,
			"first_wrong_output": 	first_wrong_output,
			"first_wrong_stdout":	first_wrong_stdout,
			"result_list":        	resultList,
			"output_list":			outputList,
			"stdout_list":			stdoutList,
		})
		if err != nil {
			log.Printf("status.html template execute error:%v", err)
			fmt.Fprintf(w, `text template execute Error`)
			return
		}
	} else {
		var statuses []StatusData
		page := 0
		limit := 20
		if r.Form.Has("page") {
			page, err = strconv.Atoi(r.Form.Get("page"))
			if err != nil {
				page = 0
			}
		}
		if r.Form.Has("limit") {
			limit, err = strconv.Atoi(r.Form.Get("limit"))
			if err != nil {
				limit = 20
			}
		}
		if r.Form.Has("search") && r.Form.Get("search") != "" {
			err = db.Select(&statuses, statusLikeSql, r.Form.Get("search"), page, limit)
		} else {
			err = db.Select(&statuses, statusSql, page, limit)
		}
		if err != nil {
			log.Printf("db select(table=status) error:%v", err)
			fmt.Fprintf(w, `Database Error`)
			return
		}
		t, err := template.ParseFiles(indexPath + "/status.html")
		if err != nil {
			log.Printf("status.html template parse error:%v", err)
			fmt.Fprintf(w, `template parse Error`)
			return
		}
		err = t.Execute(w, map[string]interface{}{
			"statuses": statuses,
			"page": page,
			"limit": limit,
		})
		if err != nil {
			log.Printf("status.html template execute error:%v", err)
			fmt.Fprintf(w, `text template execute Error`)
			return
		}
	}
}

// 展示题目总览
func index(w http.ResponseWriter, r *http.Request) {
	var err error
	var problems []Problem
	err = db.Select(&problems, problemsSql)
	if err != nil {
		log.Printf("db select(table=problems) error:%v", err)
		fmt.Fprintf(w, `Database Error`)
		return
	}
	t, err := template.ParseFiles(indexPath + "/index.html")
	if err != nil {
		log.Printf("index.html template parse error:%v", err)
		fmt.Fprintf(w, `template parse Error`)
		return
	}
	err = t.Execute(w, map[string]interface{}{
		"problems": problems,
	})
	if err != nil {
		log.Printf("index.html template execute error:%v", err)
		fmt.Fprintf(w, `text template execute Error`)
	}
}

// 展示题目详情
func problem(w http.ResponseWriter, r *http.Request) {
	var err error
	languages, err := conf.GetSection("SUPPORT")
	if err != nil {
		log.Printf("get section error:%v", err)
	}
	if r.Method == "GET" {
		r.ParseForm()
		problemIds := r.Form["problem_id"]
		if len(problemIds) == 0 {
			index(w, r)
			return
		}
		problemId := problemIds[0]
		var problem ProblemDetail
		err = db.Get(&problem, problemDetailSql, problemId)
		if err != nil {
			log.Printf("db get problem error:%v", err)
			fmt.Fprintf(w, `No related problem:%s`, problemId)
			return
		}
		funcMap := template.FuncMap{
			"intAndint": intAndint,
		}
		t, err := template.New("problem.html").Funcs(funcMap).ParseFiles(indexPath + "/problem.html")
		if err != nil {
			log.Printf("problem.html template parse error:%v", err)
			fmt.Fprintf(w, `template parse Error`)
		}
		var stdins []string
		var stdouts []string
		json.Unmarshal([]byte(problem.Stdin), &stdins)
		json.Unmarshal([]byte(problem.Stdout), &stdouts)
		err = t.Execute(w, map[string]interface{}{
			"problem":   problem,
			"intAndint": intAndint,
			"stdins":    stdins,
			"stdouts":   stdouts,
			"languages": languages,
		})
		if err != nil {
			log.Printf("problem.html template execute error:%v", err)
			fmt.Fprintf(w, `text template execute Error`)
		}
	}
}

func manager(w http.ResponseWriter, r *http.Request) {
	var err error
	var problems []Problem
	err = db.Select(&problems, problemsSql)
	if err != nil {
		log.Printf("db select error:%v", err)
		fmt.Fprintf(w, `Database Error`)
	}
	t, err := template.ParseFiles(indexPath + "/manager_problem.html")
	if err != nil {
		log.Printf("manager_problem.html template parse error:%v", err)
		fmt.Fprintf(w, `template parse Error`)
	}
	err = t.Execute(w, map[string]interface{}{
		"problems": problems,
	})
	if err != nil {
		log.Printf("manager_problem.html template execute error:%v", err)
		fmt.Fprintf(w, `text template execute Error`)
	}
}

// 修改题目信息
func updateProblem(w http.ResponseWriter, r *http.Request) {
	funcMap := template.FuncMap{
		"intAndint": intAndint,
	}
	t, err := template.New("update_problem.html").Funcs(funcMap).ParseFiles(indexPath + "/update_problem.html")
	if err != nil {
		log.Printf("update_problem.html template parse error:%v", err)
		fmt.Fprintf(w, `template parse Error`)
	}
	if r.Method == "POST" {
		r.ParseForm()
		problem := ProblemDetail{
			Ruler:       1,
			TimeLimit:   1000,
			MemoryLimit: 32 * 1024,
		}
		if _, ok := r.Form["insert"]; ok {
			err = t.Execute(w, map[string]interface{}{
				"problem": problem,
				"update":  false,
				"stdins":  []string{},
				"stdouts": []string{},
			})
			if err != nil {
				log.Printf("update_problem.html template execute error:%v", err)
				fmt.Fprintf(w, `text template execute Error`)
			}
			return
		}
		problem.Title = r.Form.Get("title")
		problem.Description = r.Form.Get("description")
		problem.DescInput = r.Form.Get("desc_input")
		problem.DescOutput = r.Form.Get("desc_output")
		var data []byte
		data, _ = json.Marshal(r.Form["stdin"])
		problem.Stdin = string(data)
		data, _ = json.Marshal(r.Form["stdout"])
		problem.Stdout = string(data)
		if r.Form.Has("time_limit") {
			problem.TimeLimit, _ = strconv.Atoi(r.Form.Get("time_limit"))
		}
		if r.Form.Has("memory_limit") {
			problem.MemoryLimit, _ = strconv.Atoi(r.Form.Get("memory_limit"))
		}
		if r.Form.Has("data_ref_id") {
			problem.DataRefId = r.Form.Get("data_ref_id")
		}
		if r.Form.Has("spj_ref_id") {
			problem.SpjRefId = r.Form.Get("spj_ref_id")
		}
		if r.Form.Has("compare_flag") {
			num := 0
			for _, v := range r.Form["compare_flag"] {
				x, _ := strconv.Atoi(v)
				num += x
			}
			problem.CompareFlag = num
		}
		if r.Form.Has("judge_type") {
			problem.JudgeType, _ = strconv.Atoi(r.Form.Get("judge_type"))
		}
		problem.Ruler, _ = strconv.Atoi(r.Form.Get("ruler"))
		if r.Form.Has("query_set") {
			num := 0
			for _, v := range r.Form["query_set"] {
				x, _ := strconv.Atoi(v)
				num += x
			}
			problem.QuerySet = num
		}
		problem.Eps = r.Form.Get("eps")
		if r.Form.Has("problem_id") {
			problem.ProblemId, _ = strconv.Atoi(r.Form.Get("problem_id"))
			_, err = db.NamedExec(updateProblemSql, problem)
			if err != nil {
				log.Printf("update problem error:%v", err)
				fmt.Fprintf(w, `update problem Error`)
			}
		} else {
			_, err = db.NamedExec(insertProblemSql, problem)
			if err != nil {
				log.Printf("insert problem error:%v", err)
				fmt.Fprintf(w, `insert problem Error`)
			}
		}
		manager(w, r)
		return
	}
	if r.Method == "GET" {
		r.ParseForm()
		problemId := r.Form.Get("problem_id")
		if problemId == "" {
			index(w, r)
			return
		}
		var problem ProblemDetail
		err = db.Get(&problem, problemDetailSql, problemId)
		if err != nil {
			log.Printf("db get problem error:%v", err)
			fmt.Fprintf(w, `No related problem:%s`, problemId)
			return
		}
		var stdins []string
		var stdouts []string
		json.Unmarshal([]byte(problem.Stdin), &stdins)
		json.Unmarshal([]byte(problem.Stdout), &stdouts)
		err = t.Execute(w, map[string]interface{}{
			"problem": problem,
			"update":  true,
			"stdins":  stdins,
			"stdouts": stdouts,
		})
		if err != nil {
			log.Printf("update_problem.html template execute error:%v", err)
			fmt.Fprintf(w, `text template execute Error`)
		}
	}
}

// 更新题目文件
func updateDatafile(w http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles(indexPath + "/update_datafile.html")
	if err != nil {
		log.Printf("update_datafile.html template parse error:%v", err)
		fmt.Fprintf(w, `template parse Error`)
	}
	r.ParseForm()
	problemId := r.Form.Get("problem_id")
	if problemId == "" {
		manager(w, r)
		return
	}
	dirs, err := ioutil.ReadDir(dataPath + "/" + problemId)
	if err != nil {
		log.Printf("read dir error:%v", err)
		log.Printf("read dir error:%s", err.Error())
		log.Printf("read dir error:%s", os.ErrNotExist.Error())
		if errors.Is(err, os.ErrNotExist) {
			err = os.MkdirAll(dataPath+"/"+problemId, 0600)
			if err != nil {
				log.Printf("read dir error:%v", err)
				fmt.Fprintf(w, `access file error`)
				return
			}
		} else {
			fmt.Fprintf(w, `access file error`)
			return
		}
	}
	var files []string
	for _, file := range dirs {
		files = append(files, file.Name())
	}
	err = t.Execute(w, map[string]interface{}{
		"files":      files,
		"problem_id": problemId,
	})
	if err != nil {
		log.Printf("update_datafile.html template execute error:%v", err)
		fmt.Fprintf(w, `text template execute Error`)
	}
}

// 修改文件接口
func fileModify(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	problemId := r.Form.Get("problem_id")
	if problemId == "" {
		fmt.Fprintf(w, `{"code" : 10, msg:"no problem id"}`)
		return
	}
	if r.Form.Has("download") {
		file := r.Form["file"][0]
		filename := dataPath + "/" + problemId + "/" + file
		log.Printf("download file:%s", filename)
		_, err := os.Stat(filename)
		if err != nil || os.IsNotExist(err) {
			log.Printf("file stat %s error: %v", filename, err)
			fmt.Fprintf(w, `{"code" : 14, msg:"no such file"}`)
			return
		}
		bytes, err := os.ReadFile(filename)
		if err != nil {
			log.Printf("read %s error: %v", filename, err)
			fmt.Fprintf(w, `{"code" : 15, msg:"read error"}`)
			return
		}
		w.Header().Add("Content-Type", "application/octet-stream")
		w.Header().Add("Content-Disposition", "attachment; filename= "+file)
		w.Write(bytes)
		return
	}
	if r.Method == "POST" {
		if err := r.ParseMultipartForm(20); err != nil {
			fmt.Println("解析url失败", err)
		}
		if r.Form.Has("upload") {
			files := r.MultipartForm.File["file"]
			for _, v := range files {
				filename := dataPath + "/" + problemId + "/" + v.Filename
				f, err := os.Create(filename)
				if err != nil {
					log.Printf("create %s error: %v", filename, err)
					fmt.Fprintf(w, `{"code" : 11, msg:"create error"}`)
					return
				}
				file, err := v.Open()
				if err != nil {
					log.Printf("open %s error: %v", filename, err)
					fmt.Fprintf(w, `{"code" : 12, msg:"open error"}`)
					return
				}
				_, err = io.Copy(f, file)
				if err != nil {
					log.Printf("copy %s error: %v", filename, err)
					fmt.Fprintf(w, `{"code" : 13, msg:"copy error"}`)
					return
				}
			}
			fmt.Fprintf(w, `{"code" : 0, msg:"upload ok"}`)
		} else if r.Form.Has("delete") {
			files := r.Form["file"]
			for _, file := range files {
				filename := dataPath + "/" + problemId + "/" + file
				err := os.Remove(filename)
				if err != nil {
					log.Printf("remove %s error: %v", filename, err)
					fmt.Fprintf(w, `{"code" : 18, msg:"delete error"}`)
					return
				}
			}
			fmt.Fprintf(w, `{"code" : 0, msg:"delete ok"}`)
		}
	}
}

var logPath string
var indexPath string
var myLogFile string
var myConfFile string
var dataPath string
var backup_interface_url string
var receive_url string

var indexEngine *template.Template
var problemEngine *template.Template
var statusEngine *template.Template
var statusDetailEngine *template.Template
var updateDatafileEngine *template.Template
var updateProblemEngine *template.Template
var managerProblem *template.Template

var statusSql = `select runid, username, result, use_time, use_memory, length, submit_time, problem_id, language
					from status order by submit_time desc limit ?,?`
var statusLikeSql = `select runid, username, result, use_time, use_memory, length, submit_time, problem_id, language
						from status where username=? order by submit_time desc limit ?,?`
var statusDetailSql = `select runid, username, result, use_time, use_memory, length, language, submit_time, problem_id, pass_example, total_example, ifnull(error, '') error, ifnull(src, '') src, ifnull(extern_info, '') extern_info, ifnull(recv_data, '') recv_data from status where runid=?`
var statusInsertSql = `insert into status(username, result, length, submit_time, problem_id, src, language) values(?,?,?,?,?,?,?)`
var statusUpdateSql = `update status set result=:result, use_time=:use_time, use_memory=:use_memory, pass_example=:pass_example, total_example=:total_example, error=:error, extern_info=:extern_info, recv_data=:recv_data where runid=:runid`
var statusUpdateResSql = `update status set result=? where runid=?`
var problemsSql = `select problem_id, title from problems`
var problemDetailSql = `select problem_id, title, description, desc_input, desc_output, stdin, stdout, time_limit, memory_limit, compare_flag, init_ref_id, data_ref_id, spj_ref_id, ruler, judge_type, eps, query_set from problems where problem_id=?`
var updateProblemSql = `update problems set title=:title, description=:description, desc_input=:desc_input, desc_output=:desc_output, stdin=:stdin, stdout=:stdout, time_limit=:time_limit, memory_limit=:memory_limit, compare_flag=:compare_flag, init_ref_id=:init_ref_id, data_ref_id=:data_ref_id, spj_ref_id=:spj_ref_id, ruler=:ruler, judge_type=:judge_type, eps=:eps, query_set=:query_set where problem_id=:problem_id`
var insertProblemSql = `insert into problems(title, description, desc_input, desc_output, stdin, stdout, time_limit, memory_limit, compare_flag, init_ref_id, data_ref_id, spj_ref_id, ruler, judge_type, eps, query_set) values(:title, :description, :desc_input, :desc_output, :stdin, :stdout, :time_limit, :memory_limit, :compare_flag, :init_ref_id, :data_ref_id, :spj_ref_id, :ruler, :judge_type, :eps, :query_set)`

func main() {
	var err error
	if len(os.Args) < 2 {
		fmt.Printf("args count must be equal 1")
		return
	}

	myConfFile = os.Args[1]

	conf, err = goconfig.LoadConfigFile(myConfFile)
	if err != nil {
		log.Fatal("ReadConfigure:", err.Error())
	}

	myLogFile, err = conf.GetValue("OJ_DEMO", "OJ_DEMO_LOGFILE")
	if err != nil || myLogFile == "" {
		log.Fatal("ReadConfigure OJ_DEMO_LOGFILE error or param is null:", err)
	}

	dataPath, err = conf.GetValue("OJ_DEMO", "OJ_DEMO_PROBLEM_FILE_PATH")
	if err != nil || dataPath == "" {
		log.Fatal("ReadConfigure OJ_DEMO_PROBLEM_FILE_PATH error or param is null:", err)
	}

	logPath, err = conf.GetValue("OJ_DEMO", "OJ_DEMO_LOGFILE_PATH")
	if err != nil || logPath == "" {
		log.Fatal("ReadConfigure OJ_DEMO_LOGFILE_PATH error or param is null:", err)
	}
	logPath = logPath + "/oj_demo"

	indexPath, err = conf.GetValue("OJ_DEMO", "OJ_DEMO_INDEX_PATH")
	if err != nil || indexPath == "" {
		log.Fatal("ReadConfigure OJ_DEMO_INDEX_PATH error or param is null:", err)
	}

	err = os.MkdirAll(logPath, 0755)
	if err != nil && !os.IsExist(err) {
		log.Fatalf("mkdir error:%v", err)
	}
	logFile, err := os.OpenFile(logPath+"/"+myLogFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0755)
	if err != nil && !os.IsExist(err) {
		panic(err)
	}
	log.SetOutput(logFile)

	webPort, err := conf.Int("WEB", "WEB_PORT")
	if err != nil {
		log.Fatal("ReadConfigure WEB_PORT:", err)
	}

	backup_interface_url, err = conf.GetValue("WEB", "BACKUP_INTERFACE_URL")
	if err != nil {
		log.Fatal("ReadConfigure BACKUP_INTERFACE_URL:", err)
	}

	receive_url, err = conf.GetValue("WEB", "RECEIVE_URL")
	if err != nil {
		log.Fatal("ReadConfigure RECEIVE_URL:", err)
	}

	err = initDB() // 调用输出化数据库的函数
	if err != nil {
		fmt.Printf("初始化失败！,err:%v\n", err)
		return
	} else {
		fmt.Printf("初始化成功")
	}

	// log.Printf("template_file:%s\n", indexPath+"/index.html")
	// indexEngine, err = template.ParseFiles(indexPath + "/index.html")
	// if err != nil {
	// 	log.Fatal("template parse index.html error:", err)
	// }

	// log.Printf("template_file:%s\n", indexPath+"/problem.html")
	// problemEngine, err = template.ParseFiles(indexPath + "/problem.html")
	// if err != nil {
	// 	log.Fatal("template parse problem.html error:", err)
	// }

	// log.Printf("template_file:%s\n", indexPath+"/status.html")
	// statusEngine, err = template.ParseFiles(indexPath + "/status.html")
	// if err != nil {
	// 	log.Fatal("template parse status.html error:", err)
	// }

	// log.Printf("template_file:%s\n", indexPath+"/status_detail.html")
	// statusDetailEngine, err = template.ParseFiles(indexPath + "/status_detail.html")
	// if err != nil {
	// 	log.Fatal("template parse status_detail.html error:", err)
	// }

	// log.Printf("template_file:%s\n", indexPath+"/update_datafile.html")
	// updateDatafileEngine, err = template.ParseFiles(indexPath + "/update_datafile.html")
	// if err != nil {
	// 	log.Fatal("template parse update_datafile.html error:", err)
	// }

	// log.Printf("template_file:%s\n", indexPath+"/update_problem.html")
	// updateProblemEngine, err = template.ParseFiles(indexPath + "/update_problem.html")
	// if err != nil {
	// 	log.Fatal("template parse update_problem.html error:", err)
	// }

	// log.Printf("template_file:%s\n", indexPath+"/manager_problem.html")
	// managerProblem, err = template.ParseFiles(indexPath + "/manager_problem.html")
	// if err != nil {
	// 	log.Fatal("template parse manager_problem.html error:", err)
	// }

	// 用来展示首页
	http.HandleFunc("/", index)
	// 展示题目详情
	http.HandleFunc("/problem", problem)
	// 接收代码结果(将此接口用来接收代码结果)
	http.HandleFunc("/receive", receiveResult)
	// 查看状态
	http.HandleFunc("/status", status)
	// 管理题目
	http.HandleFunc("/manager_problem", manager)
	// 修改题目信息
	http.HandleFunc("/update_problem", updateProblem)
	// 更新题目文件
	http.HandleFunc("/update_datafile", updateDatafile)
	// 文件上传，下载，删除链接
	http.HandleFunc("/file_modify", fileModify)
	// 在线测试接口
	http.HandleFunc("/online_test", onlineTest)
	// 代码提交接口
	http.HandleFunc("/submit_code", submitCode)
	err = http.ListenAndServe(fmt.Sprintf(":%d", webPort), nil) //监听端口
	if err != nil {
		log.Fatal("ListenAndServer:", err)
	}
}
